#include "Main\Application.h"   // standard application include
#include "Main\About.h"         // about box routines
#include "Utility\General.h"    // general utility routines
#include "Utility\Resource.h"   // resource utility routines
#include <shellapi.h>           // used for the system tray notifications
#include <VersionHelpers.h>     // used to determine OS version

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////// MAIN APPLICATION ROUTINES ///////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*/
/ / --------------------------------------------------------------------------------------------------------------------
/ / OATMEAL LICENSE AGREEMENT
/ /
/ / Copyright (c) 2015, Jeremy Falcon
/ / oatmeal.software - All rights reserved.
/ /
/ / This software application is licensed under the terms specified in the GNU General Public License version 3. If
/ / you do not have a copy of this license then you may obtain one at http://www.gnu.org/licenses/gpl-3.0.html.
/ / --------------------------------------------------------------------------------------------------------------------
/*/

// local variables
static NOTIFYICONDATA notifyIcon;

// local prototypes
static LRESULT CALLBACK __wndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static bool __procStartOptions    ();

// local macros
#define	WM_USER_SHELLICON WM_USER + 1

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*/
/ / PARAMETERS:
/ /     hInstance = handle to the current instance of the application
/ /     hPrevInstance = handle to the previous instance of the application (always NULL)
/ /     lpCmdLine = command line string the application was launched with, excluding the program name
/ /     nCmdShow = flag to specify how the main window should be shown (we ignore this)
/ /
/ / PURPOSE:
/ /     This function initializes the application and spawns the main window process.
/*/

int WINAPI
WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg = {0};      // message structure for the Widows message queue
    int nReturn = 0;    // application's return value

	//UNREFERENCED_PARAMETER(hInstance);
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
	UNREFERENCED_PARAMETER(nCmdShow);

    // process independent startup options, if returns false then exit the app
    if(__procStartOptions())
    {
        WNDCLASS wc = {0};
        HWND hWnd;

        wc.lpfnWndProc   = __wndProc;
        wc.hInstance     = hInstance;
        wc.hbrBackground = NULL;
        wc.lpszClassName = MAIN_CLASS_NAME;

        // create an invisible window to receive the system tray notifications
        if(RegisterClass(&wc) != 0)
        {
            hWnd = CreateWindow(wc.lpszClassName, NULL, WS_DISABLED, 0, 0, 0, 0, 0, 0, hInstance, NULL);
            if(hWnd != NULL)
            {
                INITCOMMONCONTROLSEX icex;
                HICON hMainIcon = LoadIcon(hInstance, (LPCTSTR)MAKEINTRESOURCE(IDI_MAINFRAME));

                // ensure that the common control DLL is loaded
                icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
                icex.dwICC  = ICC_BAR_CLASSES|ICC_DATE_CLASSES|ICC_HOTKEY_CLASS|ICC_LINK_CLASS|ICC_STANDARD_CLASSES;
                InitCommonControlsEx(&icex);

                notifyIcon.cbSize = sizeof(NOTIFYICONDATA);         // size of the structure in bytes
                notifyIcon.hWnd = hWnd;                             // window which will process messages
                notifyIcon.uID = IDI_MAINFRAME;                     // icon that will appear in the system tray
                notifyIcon.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP;   // flags
                notifyIcon.hIcon = hMainIcon;                       // icon to be displayed
                notifyIcon.uCallbackMessage = WM_USER_SHELLICON;

                // show the sucker
                Shell_NotifyIcon(NIM_ADD, &notifyIcon);

                // start the endless main application loop we use to process Windows messages with
                while(GetMessage(&msg, NULL, 0, 0) != 0)
                {
                    if(msg.hwnd == hWnd)
                    {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                    }
                }

                // let's play nice and return any message sent by windows
                nReturn = (int)msg.wParam;
            }
            else
            {
                ResourceMessage(NULL, IDS_ERR_CREATEWIN_MAIN, 0, MB_OK|MB_ICONSTOP);
                nReturn = 3;
            }
        }
        else
        {
            ResourceMessage(NULL, IDS_ERR_CREATEWIN_MAIN, 0, MB_OK|MB_ICONSTOP);
            nReturn = 2;
        }
    }
    else
        nReturn = 1;

    return nReturn;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*/
/ / PARAMETERS:
/ /     hWnd = handle to the window the message belongs to
/ /     uMsg = message to process
/ /     wParam = word sized parameter who's value depends on the message
/ /     lParam = long sized parameter who's value depends on the message
/ /
/ / PURPOSE:
/ /     Window procedure to handle messages for the main window that's invisible.
/*/

static LRESULT CALLBACK
__wndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lReturn = false;

    switch(uMsg)
    {
        case WM_COMMAND:
        {
		    int32 nId = LOWORD(wParam), nEvent = HIWORD(wParam);

            switch(nId)
            {
                case IDM_ABOUT:
                    DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutProc);
                    break;

                case IDM_EXIT:
                    Shell_NotifyIcon(NIM_DELETE, &notifyIcon);
                    DestroyWindow(hWnd);
                    break;
            }
        }
        break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        case WM_USER_SHELLICON:

            // system tray message callback
            switch(LOWORD(lParam))
            {
                case WM_RBUTTONDOWN:
                {
                    // create a pop-up menu to when right-clicked
                    alpha szAbout[MAX_LOADSTRING] = {0};
                    alpha szExit[MAX_LOADSTRING] = {0};
                    POINT clickPoint = {0};

                    HMENU hPopMenu = CreatePopupMenu();
                    HMODULE hInstance = GetModuleHandle(NULL);

                    GetCursorPos(&clickPoint);
	                LoadString(hInstance, IDS_MNU_ABOUT, szAbout, MAX_LOADSTRING);
	                LoadString(hInstance, IDS_MNU_EXIT, szExit, MAX_LOADSTRING);

                    InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION|MF_STRING, IDM_ABOUT, szAbout);
                    InsertMenu(hPopMenu, 0xFFFFFFFF, MF_SEPARATOR, IDM_SEP, NULL);
                    InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION|MF_STRING, IDM_EXIT, szExit);

                    TrackPopupMenu(hPopMenu, TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_BOTTOMALIGN,
                                   clickPoint.x, clickPoint.y, 0, hWnd, NULL);
                }
                break;
            }
            break;

        default:
            lReturn = DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

    return lReturn;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*/
/ / PURPOSE:
/ /     Processes the application defined options and command line options. Returns
/ /     false if the app needs to be shutdown or true otherwise.
/*/

static bool
__procStartOptions ()
{
    bool bReturn = true;

    // first and foremost, make sure the host OS meets our requirements
    // and, let's hope and pray you don't support anything below XP
    if(!IsWindowsXPOrGreater())
    {
        ResourceMessage(NULL, IDS_ERR_WINVER, 0, MB_OK|MB_ICONERROR);
        bReturn = false;
    }
    else
    {
        #if CONFIG_SINGLE_INSTANCE
        {
            CreateMutex(NULL, true, SINGLE_INSTANCE);

            // limit this app to a single instance only, use a mutex because there
            // is no main window and it is much safer than FindWindow() anyway
            if(GetLastError() == ERROR_ALREADY_EXISTS) bReturn = false;
        }
        #endif
    }

    return bReturn;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////